package com.hm.health.service;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

import com.goodix.callback.AbsOnHBDTestClientListener;
import com.goodix.comsdk.RomaDevice;
import com.goodix.comsdk.entity.MeasureResult;
import com.goodix.comsdk.sdk.OnHBDDataListener;
import com.goodix.comsdk.testsdk.IHBDTestClient;
import com.goodix.libromablecom.sdk.cardiff.arguments.StartArgumentsKeyEnum;
import com.goodix.utils.CommonUtils;
import com.goodix.utils.SPUtils;
import com.goodix.utils.TimeUtil;
import com.google.gson.reflect.TypeToken;
import com.hm.health.Activity.HomeActivity;
import com.hm.health.BLE.OnDeviceStatus;
import com.hm.health.MyApplication;
import com.hm.health.Utils.Constans;
import com.hm.health.Utils.FileSaveHandler;
import com.hm.health.Utils.TimerUtil;
import com.hm.health.entity.BleDevicesInfo;
import com.hm.health.entity.arguments.StartArgumentsManager;
import com.hm.health.records.TestRecordManager;
import com.hm.health.widget.InputTestConditionDialog;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class SleepService extends Service {
    public static final String TAG = "SleepService";

    private SleepBinder mBinder = new SleepBinder();
    private List<BleDevicesInfo> mDeviceInfos = new ArrayList<>();
    private FileSaveHandler mFileSaveHandler = new FileSaveHandler();
    private ArrayList mStageOutList = new ArrayList();
    private IHBDTestClient mTestClient;
    private String mInputByManuallyDeviceName;
    private TestRecordManager mTestRecordManager;
    private long mStartTestTime;
    private String mFunctionName;
    private boolean mSleepTestRunning;
    private TimerUtil mTimeUtil;
    private SleepServiceCallback mSleepServiceCallback;
    boolean is_running = false;

    public class SleepBinder extends Binder {
        public void startSleepTest() {
            if (!mSleepTestRunning) {
                startSleepMonitor();
                mSleepTestRunning = true;
                if (null != mSleepServiceCallback) {
                    mSleepServiceCallback.onSleepTestStatusChanged(true);
                }
            }
        }

        public void stopSleepTest() {
            if (mSleepTestRunning) {
                stopSleepMonitor();
                mSleepTestRunning = false;
                if (null != mSleepServiceCallback) {
                    mSleepServiceCallback.onSleepTestStatusChanged(false);
                }
            }
        }

        public void setSleepServiceCallback(SleepServiceCallback sleepServiceCallback) {
            mSleepServiceCallback = sleepServiceCallback;
        }
    }

    public interface SleepServiceCallback {
        void onTimeUpdate(String time);
        void onSleepTestStatusChanged(boolean isRunning);
        void onDetectAwake();
    }

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
        init();
    }

    private void init() {
        mSleepTestRunning = false;
        mFunctionName = StartArgumentsManager.getInstance().getFunctionName();
        mTestClient = MyApplication.getHBDTestClient();

        // 初始化测量设备列表
        // Goodix 设备
        RomaDevice device = MyApplication.getHBDTestClient().getRomaDevice();
        BleDevicesInfo devicesInfo = new BleDevicesInfo(device.getDeviceName(),
                device.getDeviceAddress());
        mDeviceInfos.add(devicesInfo);

        // 对比设备
        List<BleDevicesInfo> devicesInfos = SPUtils.getJsonObj(this,
                HomeActivity.KEY_BLE_COMPARE_DEVICES_INFO, new ArrayList<>(),
                new TypeToken<List<BleDevicesInfo>>(){}.getType());
        mDeviceInfos.addAll(devicesInfos);

        // 手动输入的对比设备
        mInputByManuallyDeviceName = InputTestConditionDialog.getDeviceName(this);
        if (!mInputByManuallyDeviceName.isEmpty()) {
            String[] names = mInputByManuallyDeviceName.split(",");
            for (int i = 0; i < names.length && i < Constans.DEVICE_ADDRESS_ARRAY.length; ++i) {
                mDeviceInfos.add(new BleDevicesInfo(names[i],
                        Constans.DEVICE_ADDRESS_ARRAY[i]));
            }
        }

        mTestRecordManager = new TestRecordManager();
    }

    private void startSleepMonitor() {
        Log.d(TAG, "startSleepMonitor begin");

        mStageOutList.clear();
        mStartTestTime = System.currentTimeMillis();

        // register.
        mTestClient.registerHBDDataListener(mAbsHBDDataListener);
        mTestClient.registerTestCallback(mOnHBDTestClientListener,null);
        setPolarDeviceListener(mOnDeviceStatus);

        // 开始存储测试记录
        mTestRecordManager.start(TimeUtil.formateTime(mStartTestTime),
                MyApplication.getInstance().getTesterInfo(), mDeviceInfos);

        // 开始保存原始数据、启动测量
        mFileSaveHandler.start(Constans.SERVER_ROMA_RAW_DATA_FOLDER,
                TimeUtil.formateTime(mStartTestTime), mDeviceInfos);

        // Start HBD.
        Bundle bundle = StartArgumentsManager.getInstance().getBundle();
        bundle.putString(StartArgumentsKeyEnum.ARG_MEASURE_FUNCTION_NAME, mFunctionName);
        mTestClient.startHBD(bundle);

        mTimeUtil = new TimerUtil(1000, () -> {
            long time = System.currentTimeMillis();
            long durationTime = Math.round((time - mStartTestTime) / 1000);
            String timeStr = CommonUtils.SecondsToTime(durationTime);
            if (mSleepServiceCallback != null) {
                mSleepServiceCallback.onTimeUpdate(timeStr);
            }
        });

        startTimer();

        Log.d(TAG, "startSleepMonitor end");
    }

    private void stopSleepMonitor() {
        Log.d(TAG, "stopSleepMonitor begin");

        stopTimer();

        mTestClient.stopHBD();

        is_running = false;

        Log.d(TAG, "stopSleepMonitor end");
    }

    private void startTimer() {
        if (mTimeUtil != null) {
            mTimeUtil.startTimer();
        }
    }

    private void stopTimer() {
        if (mTimeUtil != null) {
            mTimeUtil.cancel();
            mTimeUtil = null;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onCreate");
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return mBinder;
    }

    private void setPolarDeviceListener(OnDeviceStatus listener) {
        if (mDeviceInfos.size() > 1) {
            MyApplication.getInstance().getStandardHeartRateDevice()
                    .setOnDeviceStatus(listener);
        }
    }

    private void saveResultAndStopTest() {
        mTestRecordManager.stop();
        mFileSaveHandler.stop();
        RawdataUploadService.uploadDir(this, Constans.SERVER_ROMA_RAW_DATA_FOLDER + mFunctionName + "/");
    }

    private OnDeviceStatus mOnDeviceStatus = new OnDeviceStatus() {
        @Override
        public void onDisconnected(String address, int status) {
            Log.d(TAG, "OnDeviceStatus.onDisconnected;address:" + address + ";status:" + address);
            super.onDisconnected(address, status);
        }
    };

    private AbsOnHBDTestClientListener mOnHBDTestClientListener = new AbsOnHBDTestClientListener() {
        @Override
        public void onHBDStarted(int errCode) {
            Log.d(TAG, "AbsOnHBDTestClientListener.onHBDStarted;errCode:" + errCode);
            super.onHBDStarted(errCode);
//            doMockData();
        }

        @Override
        public void onHBDStopped(int errCode) {
            Log.d(TAG, "AbsOnHBDTestClientListener.onHBDStopped;errCode:" + errCode);
            super.onHBDStopped(errCode);

            // unregister.
            mTestClient.unRegisterTestCallback(mOnHBDTestClientListener,null);
            mTestClient.unRegisterHBDDataListener(mAbsHBDDataListener);
            setPolarDeviceListener(null);
            saveResultAndStopTest();
        }

        @Override
        public void onError(int errCode,String message) {
            Log.d(TAG, "AbsOnHBDTestClientListener.onError;errCode:" + errCode + ";message:" + message);
            super.onError(errCode, message);
        }

        @Override
        public void onDisconnected(int errCode) {
            Log.d(TAG, "AbsOnHBDTestClientListener.onDisconnected" + errCode);
        }
    };

    private OnHBDDataListener mAbsHBDDataListener = new OnHBDDataListener() {
        @Override
        public void onDataChanged(String dataType, int[] dataArr) {
            Log.d(TAG, "OnHBDDataListener.onDataChanged");
            Log.d(TAG, dataType + Arrays.toString(dataArr));
        }

        @Override
        public void onMeasureResult(MeasureResult result) {
            Log.d(TAG, "OnHBDDataListener.onMeasureResult");
            Log.d(TAG, result.toString());

            if (result.mFunctionName.equals(StartArgumentsKeyEnum.AppFunction_SLEEP)) {
                int[] property = result.mProperties;
                int ret = Integer.parseInt(result.mValue);
                Log.d(TAG, "ret:" + ret);

                for (int i = 8; i < property.length; i++) {
                    mStageOutList.add(property[i]);
                }

                if (ret == 2 || ret == 3) {
                    if (ret == 3 && null != mSleepServiceCallback) {
                        mSleepServiceCallback.onDetectAwake();
                    }
                }
            }
        }
    };

    public static int[] StringtoInt(String[] strArray){
        if(strArray == null){
            return new int[0];
        }
        int[] result = new int[strArray.length - 1]; // discard the first data.
        for (int i = 0; i < result.length; i++) {
            result[i] = Integer.valueOf(strArray[i + 1]);
        }
        return result;
    }

    private byte[] IntToByte(int[] array) {
        byte[] target = new byte[142];
        target[0] = (byte)0xaa;
        target[1] = (byte)0x11;
        target[2] = (byte)0x08;
        target[3] = (byte)0x89;
        target[4] = (byte)0x15;
        target[5] = (byte)0x87;

        target[6] = (byte)array[0];

        for (int i = 0; i < 3; i++) {
            target[6 + i * 2 + 1] = (byte) (array[ i + 1 ] >> 8 & 0xFF);
            target[6 + i * 2 + 2] = (byte) (array[ i + 1 ]& 0xFF);
        }

        for (int i = 0; i < 16; i++) {
            target[12 + i * 4 + 1] = (byte) (0x08 * i);
            target[12 + i * 4 + 2] = (byte) (array[ i + 4 ] >> 16 & 0xFF);
            target[12 + i * 4 + 3] = (byte) (array[ i + 4 ] >> 8 & 0xFF);
            target[12 + i * 4 + 4] = (byte) (array[ i + 4 ] & 0xFF);
        }

        for (int i = 0; i < 16; i++) {
            target[76 + i * 4 + 1] = (byte) (array[ i + 16 + 36 ] & 0xFF);
            target[76 + i * 4 + 2] = (byte) (array[ i + 16 + 36 ] >> 8 & 0xFF);
            target[76 + i * 4 + 3] = (byte) (array[ i + 16 + 36 ] >> 16 & 0xFF);
            target[76 + i * 4 + 4] = (byte) (array[ i + 16 + 36 ] >> 24 & 0xFF);
        }

        target[141] = getCRC(target, 141);

        return target;
    }

    byte getCRC(byte data[], int len) {
        int mCrc8Tab[] = {
                0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
                0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
                0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
                0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
                0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
                0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
                0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
                0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
                0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
                0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
                0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
                0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
                0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
                0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
                0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
                0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3,
        };

        int ubCRC8 = 0xFF;
        for (int i = 0; i < len; i++) {
            int nIndex = (ubCRC8 ^ (data[i])) & 0xFF;
            ubCRC8 = mCrc8Tab[nIndex];
        }
        return (byte) ubCRC8;
    }

}

